﻿using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using System.Text;

public class MidiDriver
{
    public static MidiDriver sharedInstance;

#if UNITY_STANDALONE_WIN
    private int handle = 0;
#elif UNITY_ANDROID
    private AndroidJavaObject midiDriver;
    private OnMidiStartListener listener;
    public delegate void onMidiStartDel();
#endif

    private bool isPlaying;

    public bool IsPlaying
    {
        get
        {
            return isPlaying;
        }
        set
        {
            isPlaying = value;
        }
    }

    private float speed;

    public float Speed
    {
        get
        {
            return speed;
        }
        set
        {
            speed = value;
        }
    }

    public MidiDriver(float speed = 1000)
    {
#if UNITY_ANDROID
        midiDriver = new AndroidJavaObject("org.billthefarmer.mididriver.MidiDriver");
#endif
        this.speed = speed;
        sharedInstance = this;
    }

    public void Start()
    {
#if UNITY_STANDALONE_WIN
        Debug.Log("Start midi driver");
        MMRESULT result = midiOutOpen(ref handle, 0, null, 0, 0);
        if (result != MMRESULT.MMSYSERR_NOERROR)
        {
            Debug.Log("Failed open a MIDI output device for playback. Code: " + result);
        }
#elif UNITY_ANDROID
        midiDriver.Call("start");
#elif UNITY_STANDALONE_OSX || UNITY_IOS
		string path = "file://" + Application.streamingAssetsPath.Replace("/", "//") + "//GrandPiano.sf2";
		_Start (path);
#endif
    }

    public void Stop()
    {
#if UNITY_STANDALONE_WIN
        Debug.Log("Stop midi driver");
        MMRESULT result = midiOutReset(handle);
        if (result != MMRESULT.MMSYSERR_NOERROR)
        {
            Debug.Log("Failed to reset MIDI device. Code: " + result);
        }
        else
        {
            result = midiOutClose(handle);
            if (result != MMRESULT.MMSYSERR_NOERROR)
            {
                Debug.Log("Failed to close MIDI device. Code: " + result);
            }
        }
#elif UNITY_ANDROID
        midiDriver.Call("stop");
#elif UNITY_STANDALONE_OSX || UNITY_IOS
		_Stop();
#endif
    }

#if UNITY_ANDROID
    public void SetOnMidiStartListener(OnMidiStartListener l)
	{
		this.listener = l;
	}
#endif

    /// <summary>
    /// b[0] = 0x90 if noteOn, 0x80 if noteOff
    /// b[1] - note to play. 0x3C is middle C
    /// b[2] - velocity or volume of note. 0x7F is maximum, 0x00 is minimum
    /// As int, it is presented 0x007F3C90 to play middle C with maximum volume
    /// </summary>
    /// <param name="b"></param>
    /// <returns></returns>
    public bool Write(byte[] b)
    {
#if UNITY_STANDALONE_WIN
        int data = b[2] << 16;
        data += b[1] << 8;
        data += b[0];
        midiOutShortMsg(handle, data);
        return true;
#elif UNITY_ANDROID
        return midiDriver.Call<bool>("write", b);
#elif UNITY_IOS || UNITY_STANDALONE_OSX
		int data = b [2] << 16;
		data += b [1] << 8;
		data += b [0];
		_Write (data);
		return true;
#endif
    }

#if UNITY_ANDROID
    public class OnMidiStartListener : AndroidJavaProxy
    {
        public onMidiStartDel OnMidiStart;

        public OnMidiStartListener()
            : base("org.billthefarmer.mididriver.MidiDriver$OnMidiStartListener")
        {
        }

        void onMidiStart()
        {
            if (OnMidiStart != null)
            {
                OnMidiStart();
            }
        }
    }
#endif

    #region DllImport_Win

#if UNITY_STANDALONE_WIN
    private delegate void MidiCallback(int handle, int msg, int instance, int param1, int param2);

    [DllImport("winmm.dll")]
    private static extern MMRESULT midiOutOpen(ref int handle, int deviceID, MidiCallback proc, int instance, int flags);

    [DllImport("winmm.dll")]
    private static extern MMRESULT midiOutClose(int handle);

    [DllImport("winmm.dll")]
    private static extern MMRESULT midiOutReset(int handle);

    [DllImport("winmm.dll")]
    private static extern int midiOutShortMsg(int handle, int message);

	[DllImport("winmm.dll")]
	private static extern long mciSendString(string command, StringBuilder returnValue, int returnLength, System.IntPtr winHandle);
#endif
    #endregion

    #region DllImport_iOS

#if UNITY_IOS
	[DllImport ("__Internal")]
	private static extern void _Write (int msg);

	[DllImport ("__Internal")]
	private static extern void _Start (string pathToSoundFont);

	[DllImport ("__Internal")]
	private static extern void _Stop ();
#endif
    #endregion

    #region DllImport_OSX

#if UNITY_STANDALONE_OSX
	[DllImport ("pianomacplugin")]
	private static extern void _Write (int msg);

	[DllImport ("pianomacplugin")]
	private static extern void _Start (string pathToSoundFont);

	[DllImport("pianomacplugin")]
	private static extern void _Stop ();
#endif
    #endregion
}
